CLI开发:处理 data.ts 的文件
概述
本节继续完善 CLI 模板生成工具,处理 data.ts 文件的生成逻辑。重点解决 EJS 模板渲染、基础目录遍历缺失等问题,并对 configPage 参数进行精确的类型判断。
EJS 模板渲染
处理 .ejs 文件
当遇到 .ejs 后缀的文件时,使用 EJS 引擎结合用户配置的 options 进行模板渲染:
import ejs from 'ejs'
import fs from 'fs-extra'
import path from 'path'
// 判断是否为 EJS 模板文件
if (currentPath.endsWith('.ejs')) {
// 读取模板内容
const template = await fs.readFile(currentPath, 'utf-8')
// 使用 options 作为渲染数据
const result = ejs.render(template, options)
// 输出到目标路径(去掉 .ejs 后缀)
const destPath = currentPath.replace('.ejs', '')
await fs.writeFile(destPath, result)
}
typescript
EJS 模板示例
// templates/router/data.ts.ejs
export const routerConfig = {
mode: '<%= router.mode %>',
<%_ if (router.guard) { _%>
guard: true,
<%_ } _%>
}
typescript
修正 base 目录遍历
问题
用户传递的 options 中 configPage 是一个数组,但原代码未遍历 base 基础目录。
修复
// 精确判断数组类型
if (configPage instanceof Array) {
// 1. 先处理基础目录(始终需要读取)
const basePath = path.join(templatesDir, 'base')
await workFiles(basePath, options)
// 2. 再遍历用户选择的配置目录
for (const [fileName, restFileName] of Object.entries(configPage)) {
if (typeof restFileName === 'string') {
const srcPath = path.join(templatesDir, fileName, restFileName)
await workFiles(srcPath, options)
} else if (restFileName === true) {
const srcPath = path.join(templatesDir, fileName)
await workFiles(srcPath, options)
}
}
}
typescript
workFiles 函数实现
async function workFiles(src: string, options: ProcessOptions): Promise<void> {
const entries = await fs.readdir(src, { withFileTypes: true })
for (const entry of entries) {
const currentPath = path.join(src, entry.name)
if (entry.isDirectory()) {
await workFiles(currentPath, options) // 递归处理子目录
} else if (entry.name.endsWith('.ejs')) {
// EJS 模板渲染
const template = await fs.readFile(currentPath, 'utf-8')
const result = ejs.render(template, options)
const outputPath = currentPath
.replace(templatesDir, options.dest)
.replace('.ejs', '')
await fs.ensureDir(path.dirname(outputPath))
await fs.writeFile(outputPath, result)
} else {
// 普通文件直接复制
const outputPath = currentPath.replace(templatesDir, options.dest)
await fs.ensureDir(path.dirname(outputPath))
await fs.copy(currentPath, outputPath)
}
}
}
typescript
调试技巧
断点调试
在关键行(如 148 行)设置断点,使用 VS Code 的调试功能:
// .vscode/launch.json
{
"type": "node",
"request": "launch",
"name": "Debug CLI",
"program": "${workspaceFolder}/bin/create-vp.js",
"args": ["test-project"]
}
json
常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 文件未被处理 | base 目录未被遍历 | 添加 basePath 单独处理 |
.ejs 文件原样拷贝 | 缺少后缀判断 | 添加 .ejs 文件过滤逻辑 |
| 配置未生效 | configPage 类型判断不精确 | 使用 instanceof Array |
关键要点
data.ts.ejs模板通过 EJS 引擎结合用户选项渲染生成最终文件- base 目录是默认必须处理的基础模板,需在遍历 configPage 之前单独处理
configPage使用instanceof Array精确判断,避免类型错误- 递归处理子目录时需区分 EJS 模板文件和普通文件
↑